﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using Airborne.ScrumPoker.Model.TeamFoundation;
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace Airborne.ScrumPoker.Module.TeamFoundation.Model
{
    public class TeamFoundationProxy : ITeamFoundationProxy, IDisposable
    {
        private const string DefaultWorkItemTypeName = "Product Backlog Item";

        #region Constructor
        public TeamFoundationProxy(TeamFoundationConfiguration configuration)
        {
            Guard.IsNotNull(configuration, "configuration");
            this.Configuration = configuration;
        }
        #endregion

        #region Properties
        private TeamFoundationServer TeamFoundationServer { get; set; }
        private TeamFoundationConfiguration Configuration { get; set; }


        /// <summary>
        /// Gets a value indicating whether a connection has been established to the server.
        /// </summary>
        public bool IsConnected
        {
            get
            {
                if (TeamFoundationServer != null)
                {
                    return TeamFoundationServer.HasAuthenticated;
                }
                return false;
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Establish a connection to the server and Authenticates.
        /// </summary>
        public bool Connect()
        {
            if (!IsConnected)
            {
                return ConnectToServer(Configuration);
            }
            return false;
        }

        /// <summary>
        /// Retrieve workitems based on the default type : Product Backlog Item
        /// </summary>
        public ITeamWorkItem[] GetWorkItems(ITeamProject project)
        {
            return GetWorkItems(project, DefaultWorkItemTypeName);
        }

        /// <summary>
        /// Retrieve workitems based on a provided workitem name.
        /// </summary>
        public ITeamWorkItem[] GetWorkItems(ITeamProject project, string workItemType)
        {
            Guard.IsNotNull(project, "project");
            Guard.IsNotNullOrEmpty(workItemType, "workItemName");

            EnsureConnected();

            WorkItemStore workItemStore = (WorkItemStore)TeamFoundationServer.GetService(typeof(WorkItemStore));

            string workItemQuery = string.Format(CultureInfo.InvariantCulture, " SELECT [System.Id], [System.Title]," +
               " [Conchango.TeamSystem.Scrum.BusinessPriority], [Conchango.TeamSystem.Scrum.DeliveryOrder]" +
               " FROM WorkItems " +
               " WHERE [System.TeamProject] = '{0}' AND [System.WorkItemType] = '{1}'" +
               " ORDER BY [System.WorkItemType], [System.Id]", project.Name, workItemType);

            WorkItemCollection workItemCollection = workItemStore.Query(workItemQuery);

            IList<ITeamWorkItem> workItems = new List<ITeamWorkItem>();
            foreach (WorkItem workItem in workItemCollection)
            {
                workItems.Add(new ProductBacklogItem(workItem));
            }

            return workItems.ToArray();
        }

        /// <summary>
        /// Gets a list of all the projects.
        /// </summary>
        public ITeamProject[] GetProjects()
        {
            VersionControlServer versionControl = (VersionControlServer)TeamFoundationServer.GetService(typeof(VersionControlServer));
            var projects = from p in versionControl.GetAllTeamProjects(false)
                           select new TeamFoundationProject(p);

            return projects.ToArray();
        }

        private bool ConnectToServer(TeamFoundationConfiguration configuration)
        {
            try
            {
                TeamFoundationServer = TeamFoundationServerFactory.GetServer(configuration.Server, new UICredentialsProvider());

                TeamFoundationServer.Authenticate();
                return true;
            }
            catch (TeamFoundationServerUnauthorizedException)
            {
                //unauthorised
            }
            catch (TeamFoundationInvalidServerNameException)
            {
                //invalid server
            }
            catch (WebException)
            {
                //could not connect..
            }
            return false;
        }

        private void EnsureConnected()
        {
            if (!IsConnected)
            {
                Connect();
            }
        }
        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (IsConnected)
                {
                    TeamFoundationServer.Dispose();
                }
            }
        }
        #endregion

    }
}
